package top.z.template.parse;

import top.z.template.Error;
import top.z.template.Source;

import java.util.List;

/**
 * 便利 Token({@link Token}) 集合
 */
public class TokenStream {

    private final List<Token> token;

    private int index = 0;
    private int total = 0;

    public TokenStream(List<Token> token) {
        this.token = token;
        this.index = 0;
        this.total = token.size();
    }

    /**
     * 在流中是否还有未处理的 Token
     */
    public boolean hasNext() {
        return index < total;
    }

    /**
     * 消费下一个 Token 并且返回它
     */
    public Token consume() {
        if (hasNext()) {
            return token.get(index++);
        } else {
            throw new RuntimeException("Reached the total of the source.");
        }
    }

    /**
     * 检测下一个 Token 是否有具体的类型并且消费它, 如果下一个标记与类型不匹配则抛出异常
     */
    public Token expect(TokenType type) {
        boolean result = match(type, true);
        if (!result) {
            Token token = index < this.token.size() ? this.token.get(index) : null;
            Span  span  = token != null ? token.getSpan() : null;
            if (span == null) {
                throw Error.error("Expected '" + type.key() + "', but reached the total of the source.", this);
            } else {
                throw Error.error("Expected '" + type.key() + "', but got '" + token.getText() + "'", span);
            }
        }
        return token.get(index - 1);
    }

    /**
     * 检测下一个 Token 是否与指定的文本匹配并且消费它, 如果下一个 Token 与指定文本不匹配则抛出异常
     */
    public Token expect(String text) {
        boolean result = match(text, true);
        if (!result) {
            Token token = index < this.total ? this.token.get(index) : null;
            Span  span  = token != null ? token.getSpan() : null;
            if (span == null) {
                throw Error.error("Expected '" + text + "', but reached the total of the source.", this);
            } else {
                throw Error.error("Expected '" + text + "', but got '" + token.getText() + "'", span);
            }
        }
        return token.get(index - 1);
    }

    /**
     * 匹配下一个 Token 是否与指定的类型一致, 可选是否消费
     *
     * @param type    类型
     * @param consume 是否消费
     * @return Boolean
     */
    public boolean match(TokenType type, boolean consume) {
        if (index >= total) {
            return false;
        }
        if (token.get(index).getType() == type) {
            if (consume) {
                index++;
            }
            return true;
        }
        return false;
    }

    /**
     * 匹配下一个 Token 是否与指定的文本一致, 可选是否消费
     *
     * @param text    文本
     * @param consume 是否消费
     * @return Boolean
     */
    public boolean match(String text, boolean consume) {
        if (index >= total) {
            return false;
        }
        if (token.get(index).getText().equals(text)) {
            if (consume) {
                index++;
            }
            return true;
        }
        return false;
    }

    /**
     * 匹配任意一种 Token 类型, 如果匹配到的话可选择是否消费下一个 Token
     *
     * @param consume 是否消费
     * @param type    类型
     * @return Boolean
     */
    public boolean match(boolean consume, TokenType... type) {
        for (TokenType item : type) {
            if (match(item, consume)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 匹配任意一种 Token 类型, 如果匹配到的话可选择是否消费下一个 Token
     *
     * @param consume 是否消费
     * @param text    文本
     * @return Boolean
     */
    public boolean match(boolean consume, String... text) {
        for (String item : text) {
            if (match(item, consume)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 返回{@link Source}
     */
    public Source getSource() {
        if (token.isEmpty()) {
            return null;
        }
        return token.get(0).getSpan().getSource();
    }

    /**
     * 返回{@link Span}
     */
    public Span getSpan() {
        if (token.isEmpty()) {
            return null;
        }
        return token.get(index).getSpan();
    }
}
